package cn.cms.controller;

import cn.cms.constants.Constants;
import cn.cms.util.StringUtil;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import org.codehaus.groovy.control.CompilationFailedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@Controller
public class GroovyController extends AbstractController {

    /**
     * 类加载器
     */
    private ClassLoader classLoader = null;

    /**
     * 默认构造函数
     */
    public GroovyController() {
        classLoader = this.getClass().getClassLoader();
        // 构造文件监听器
    }

    /**
     * 执行Groovy脚本调用入口，执行动态脚本
     * <p/>
     * 1、.gv结尾的方法处理入口，获取请求的文件名、方法名称
     * <p/>
     * 2、根据URL分析获取请求对应的Groovy文件
     * <p/>
     * 3、调用方法{@link GroovyController}获取本次执行的脚本方法
     * <p/>
     * 4、动态执行脚本，并返回处理结果
     *
     * @param request    请求信息
     * @param response   响应信息
     * @param fileName   文件名
     * @param methodName 调用方法名
     * @return 处理结果
     */
    @RequestMapping(value = "**/{fileName}-{methodName}.gv")
    public Object doAction(HttpServletRequest request, HttpServletResponse response,
                           @PathVariable("fileName") String fileName, @PathVariable("methodName") String methodName) {
        long startTime = 0;
        // 方法开始日志
        if (logger.isDebugEnabled()) {
            startTime = (new java.util.Date()).getTime();
            logger.debug("处理请求【" + fileName + "-" + methodName + "】开始");
        }
        // 设置不缓存请求内容
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragrma", "no-cache");
        response.setDateHeader("Expires", 0);

        // 返回值
        Object result = null;
        /* 获取访问的路径 */
//        int rootPath = request.getContextPath().length() + 7;
//        String requestUri = request.getRequestURI();
//        int endIndex = requestUri.length() - methodName.length() - 4 - fileName.length();
//        String filePath = requestUri.substring(rootPath, endIndex) + "controller/" + fileName;
//        filePath = filePath.replaceAll("/", ".");

        Object grovyObject = loadScript(fileName);
        Method localMethod = getScriptMethod(grovyObject, methodName);
        try {
            result = localMethod.invoke(grovyObject, new Object[]{request, response});
            // 记录系统日志
//            ReqBo reqBo = super.initReqBo(request);
//            reqBo.addParam("BsLoginoprRd.dealFlag", "0"); // 1:异常 , 0：正常
//            super.doRecordLog(reqBo);
            if (logger.isDebugEnabled()) {
                long useredTime = (new java.util.Date()).getTime() - startTime;
                logger.debug("处理请求【" + fileName + "-"
                        + methodName + "】结束,耗时【"
                        + useredTime + "】ms, 处理结果reuslut=\n" + result);
            }
        } catch (IllegalArgumentException e) {
            logger.error("调用Groovy方法参数错误", e);
            // 记录系统异常日志
//            ReqBo reqBo = super.initReqBo(request);
//            reqBo.addParam("BsLoginoprRd.dealFlag", "1"); // 1:异常 , 0：正常
//            super.doRecordLog(reqBo);
            throw new RuntimeException("不合法或不正确的参数:Groovy");
        } catch (IllegalAccessException e) {
            logger.error("Groovy方法无权限访问", e);
            // 记录系统异常日志
//            ReqBo reqBo = super.initReqBo(request);
//            reqBo.addParam("BsLoginoprRd.dealFlag", "1"); // 1:异常 , 0：正常
//            super.doRecordLog(reqBo);
            throw new RuntimeException("方法无法访问指定类、字段、方法或构造方法的定义:Groovy");
        } catch (InvocationTargetException e) {
            logger.error("Groovy对象动态调用错误", e);
            if (e.getCause() instanceof RuntimeException) {
                throw (RuntimeException) e.getCause();
            } else {
                throw new RuntimeException("对象调用错误:Groovy");
            }
        }
        // 返回处理结果
        return result;
    }

    /**
     * 根据请求包路径加载编译脚本
     *
     * @param filePackage 请求文件包路径
     * @return 脚本实例对象
     */
    private Object loadScript(String filePackage) {
        // 方法开始日志
        if (logger.isDebugEnabled()) {
            logger.debug("方法【loadScript】处理开始,入参;[filePackage]=" + filePackage);
        }
        // 返回值
        Object scriptObject = null;

        Class groovyClass;
        // 获取脚本文件信息
        // 对象没有缓存
        GroovyClassLoader loader = new GroovyClassLoader(classLoader);
        // 获取文件名
        String fileName = StringUtil.formatFilePath(Constants.GROOVY_PATH) + filePackage + ".groovy";
        logger.info("加载groovy文件：" + fileName);
        try {
            groovyClass = loader.parseClass(new GroovyCodeSource(new File(fileName), "UTF-8"));
        } catch (CompilationFailedException e) {
            throw new RuntimeException("编译失败：" + fileName
                    + "脚本");
        } catch (IOException e) {
            throw new RuntimeException("文件读取失败：" + fileName + "脚本");
        }

        try {
            scriptObject = groovyClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("实例化失败" + "Groovy脚本");
        } catch (IllegalAccessException e) {
            throw new RuntimeException("方法无法访问指定类、字段、方法或构造方法的定义:Groovy脚本");
        }
        // 方法开始日志
        if (logger.isDebugEnabled()) {
            logger.debug("方法【loadScript】处理结束, 出参：[scriptObject]=" + scriptObject);
        }
        return scriptObject;
    }

    /**
     * 获取脚本调用对象
     *
     * @param scriptObject 脚本实例
     * @param methodName   脚本方法
     * @return 返回方法
     */
    private Method getScriptMethod(Object scriptObject, String methodName) {
        // 方法开始日志
        if (logger.isDebugEnabled()) {
            logger.debug("调用方法【getScriptMethod】开始scriptObject=" + scriptObject + "; methodName="
                    + methodName);
        }
        Method localMethod = null;
        try {
            localMethod = scriptObject.getClass().getDeclaredMethod(methodName,
                    new Class[]{HttpServletRequest.class, HttpServletResponse.class});
        } catch (SecurityException e) {
            logger.error("获取Groovy方法错误", e);
            throw new RuntimeException("安全管理,无法访问:Groovy脚本");
        } catch (NoSuchMethodException e) {
            logger.error("Groovy脚本中方法不存在", e);
            throw new RuntimeException("Groovy脚本中方法不存在:Groovy脚本");
        }
        // 方法开始日志
        if (logger.isDebugEnabled()) {
            logger.debug("调用方法【getScriptMethod】结束localMethod=" + localMethod);
        }
        return localMethod;
    }
}
